/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace DaveSexton.DocProject
{
	public sealed class CapturedBuildTrace : BuildTrace
	{
		#region Public Properties
		public string Output
		{
			get
			{
				return output.ToString();
			}
		}

		public ICollection<CapturedBuildEvent> Events
		{
			get
			{
				return new System.Collections.ObjectModel.ReadOnlyCollection<CapturedBuildEvent>(events);
			}
		}

		/// <value>Percent complete.</value>
		public int Progress
		{
			get
			{
				return progress;
			}
		}

		public string Status
		{
			get
			{
				return status;
			}
		}
		#endregion

		#region Private / Protected
		private readonly StringBuilder output = new StringBuilder();
		private readonly List<CapturedBuildEvent> events = new List<CapturedBuildEvent>();
		private int progress;
		private string status;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="CapturedBuildTrace" /> class.
		/// </summary>
		public CapturedBuildTrace()
		{
		}
		#endregion

		#region Methods
		public void Clear()
		{
			output.Remove(0, output.Length);
			events.Clear();
		}

		public override void TraceLine()
		{
			output.Append(Environment.NewLine);
		}

		public override void TraceLine(string message)
		{
			output.Append(message + Environment.NewLine);
		}

		public override void TraceLine(string format, params object[] args)
		{
			output.AppendFormat(System.Globalization.CultureInfo.CurrentCulture, format + Environment.NewLine, args);
		}

		public override void Trace(string message)
		{
			output.Append(message);
		}

		public override void Trace(string format, params object[] args)
		{
			if (args == null)
				output.Append(format);
			else
				output.AppendFormat(System.Globalization.CultureInfo.CurrentCulture, format, args);
		}

		public override void TracePath(string path)
		{
			output.Append(Environment.ExpandEnvironmentVariables(path));
		}

		public override void TracePathLine(string path)
		{
			output.Append(Environment.ExpandEnvironmentVariables(path) + Environment.NewLine);
		}

		public override void TraceEvent(System.Diagnostics.TraceEventType type, string category, string message, string file, int line, string userMessage)
		{
			events.Add(new CapturedBuildEvent(type, category, message, file, line, userMessage));
		}

		public override void FlushEvents()
		{
			OnFlush(EventArgs.Empty);
		}

		public override void SetProgress(string text, int completed, int total)
		{
			progress = checked(completed * 100 / total);

			SetStatus(text);

			OnProgressChanged(EventArgs.Empty);
		}

		public override void SetStatus(string text)
		{
			if ((status == null && text != null) || !status.Equals(text, StringComparison.CurrentCultureIgnoreCase))
			{
				status = text;

				OnStatusChanged(EventArgs.Empty);
			}
		}
		#endregion

		#region Events
		private readonly object FlushEventLock = new object();
		private EventHandler FlushEvent;
		private readonly object StatusChangedEventLock = new object();
		private EventHandler StatusChangedEvent;
		private readonly object ProgressChangedEventLock = new object();
		private EventHandler ProgressChangedEvent;

		/// <summary>
		/// Event raised after the <see cref="Progress" /> property value has changed.
		/// </summary>
		public event EventHandler ProgressChanged
		{
			add
			{
				lock (ProgressChangedEventLock)
				{
					ProgressChangedEvent += value;
				}
			}
			remove
			{
				lock (ProgressChangedEventLock)
				{
					ProgressChangedEvent -= value;
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="ProgressChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
		private void OnProgressChanged(EventArgs e)
		{
			EventHandler handler = null;

			lock (ProgressChangedEventLock)
			{
				handler = ProgressChangedEvent;

				if (handler == null)
					return;
			}

			handler(this, e);
		}

		/// <summary>
		/// Event raised after the <see cref="Status" /> property value has changed.
		/// </summary>
		public event EventHandler StatusChanged
		{
			add
			{
				lock (StatusChangedEventLock)
				{
					StatusChangedEvent += value;
				}
			}
			remove
			{
				lock (StatusChangedEventLock)
				{
					StatusChangedEvent -= value;
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="StatusChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
		private void OnStatusChanged(EventArgs e)
		{
			EventHandler handler = null;

			lock (StatusChangedEventLock)
			{
				handler = StatusChangedEvent;

				if (handler == null)
					return;
			}

			handler(this, e);
		}

		/// <summary>
		/// Event raised when the <see cref="Events" /> list should be committed to its target.
		/// </summary>
		public event EventHandler Flush
		{
			add
			{
				lock (FlushEventLock)
				{
					FlushEvent += value;
				}
			}
			remove
			{
				lock (FlushEventLock)
				{
					FlushEvent -= value;
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="Flush" /> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
		private void OnFlush(EventArgs e)
		{
			EventHandler handler = null;

			lock (FlushEventLock)
			{
				handler = FlushEvent;

				if (handler == null)
					return;
			}

			handler(this, e);
		}
		#endregion
	}
}
